package com.ctshk.rpc.payment.strategy.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.utils.Assert;
import com.ctshk.rpc.payment.dto.PayDetailsDTO;
import com.ctshk.rpc.payment.dto.RefundDetailsDTO;
import com.ctshk.rpc.payment.dto.UnionPayConfig;
import com.ctshk.rpc.payment.dto.rep.unionpay.UnionPayCallback;
import com.ctshk.rpc.payment.dto.rep.unionpay.UnionPayQueryTransDTO;
import com.ctshk.rpc.payment.dto.req.PayRefundReq;
import com.ctshk.rpc.payment.dto.req.unionpay.UnionPayQueryTransReq;
import com.ctshk.rpc.payment.dto.req.unionpay.UnionPayRefundReq;
import com.ctshk.rpc.payment.entity.PayRecord;
import com.ctshk.rpc.payment.entity.RefundRecord;
import com.ctshk.rpc.payment.enums.PayOrderStatus;
import com.ctshk.rpc.payment.enums.PaymentMethod;
import com.ctshk.rpc.payment.enums.RefundStatus;
import com.ctshk.rpc.payment.factory.PayDetailsDTOSimpleFactory;
import com.ctshk.rpc.payment.factory.RefundDetailsDTOSimpleFactory;
import com.ctshk.rpc.payment.mapper.RefundRecordMapper;
import com.ctshk.rpc.payment.strategy.PayStrategy;
import com.ctshk.rpc.payment.utils.unionpay.SDKUtil;
import com.ctshk.rpc.payment.utils.unionpay.UnionPayConstant;
import com.ctshk.rpc.payment.utils.unionpay.UnionPayUtil;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * @BelongsProject: ctshk-cloud
 * @BelongsPackage: com.ctshk.rpc.payment.strategy.impl
 * @Author: hef
 * @CreateTime: 2021-05-06 15:31
 * @Description: 银联支付
 */
public abstract class UnionPayStrategy extends PayStrategy<UnionPayConfig> {
    /**
     * 查询交易
     */
    private static final String QUERY_TRANS_ADDR = "/gateway/api/queryTrans.do";
    /**
     * 退款
     */
    private static final String REFUND_ADDR = "/gateway/api/backTransReq.do";

    @Resource
    private RefundRecordMapper refundRecordMapper;

    @Value("${spring.profiles.active}")
    private String active;

    public UnionPayStrategy() {
        super(UnionPayConfig.class, PaymentMethod.UNIONPAY_ONLINE_PAYMENT_WAP);
    }


    @Override
    protected boolean payCallback(Map paramMap) {
        String callbackJson = JSON.toJSONString(paramMap);
        UnionPayCallback callback = JSON.parseObject(callbackJson, UnionPayCallback.class);
        //校验签名
        Assert.isTrue(SDKUtil.validate(paramMap, callback.getEncoding()), SystemError.PAY_22005);
        //获取订单记录
        PayRecord payRecord = payRecordMapper.selectByOrderId(Long.valueOf(callback.getOrderId()));
        Assert.nonNull(payRecord, SystemError.PAY_22004);
        //校验金额
        BigDecimal amt = BigDecimal.valueOf(Long.valueOf(callback.getTxnAmt())).divide(BigDecimal.valueOf(100));
        /*if ("local".equals(active) || "test".equals(active)) {
            Assert.isTrue(amt.compareTo(new BigDecimal("0.1")) == 0, SystemError.PAY_22015);
        } else {
            Assert.isTrue(amt.compareTo(payRecord.getTotalFee()) == 0, SystemError.PAY_22015);
        }*/
        Assert.isTrue(amt.compareTo(payRecord.getTotalFee()) == 0, SystemError.PAY_22015);
        //支付状态
        PayOrderStatus status = UnionPayConstant.RESP_CODE_SUCCESS.equals(callback.getRespCode())
                ? PayOrderStatus.FULFIL : PayOrderStatus.PAYMENT_ERROR;
        //修改支付状态
        PayRecord update = new PayRecord();
        update.setId(payRecord.getId());
        update.setOrderStatus(status.getStatus());
        update.setCallbackParam(callbackJson);
        update.setGmtModified(LocalDateTime.now());
        update.setPayTime(LocalDateTime.now());
        int i = payRecordMapper.updateById(update);
        return i > 0;
    }

    @Override
    public PayDetailsDTO getPayDetails(String orderNumber) {
        UnionPayQueryTransDTO unionPayQueryTransDTO = queryTrans(orderNumber);
        return PayDetailsDTOSimpleFactory.produce(unionPayQueryTransDTO);
    }

    /**
     * 查询交易(包括支付、退货、消费撤销交易)
     *
     * @param orderNumber
     * @return
     */
    private UnionPayQueryTransDTO queryTrans(String orderNumber) {
        UnionPayConfig config = getConfig();
        UnionPayQueryTransReq req = new UnionPayQueryTransReq(config.getMerId(), orderNumber);
        Map<String, String> stringStringMap = UnionPayUtil.signData(req.toMap());
        String reqUrl = config.getRequestHostUrl() + QUERY_TRANS_ADDR;
        Map<String, String> rspData = UnionPayUtil.submitUrl(stringStringMap, reqUrl);
        System.err.println("查询交易返回数据------------" + JSON.toJSONString(rspData));
        return JSON.parseObject(JSON.toJSONString(rspData), UnionPayQueryTransDTO.class);
    }


    @Override
    public boolean refund(PayRefundReq req) {
        //TODO 商户未开通线上退款 直接抛异常
//        Assert.isTrue(false, SystemError.PAY_22012,"银联支付");
        UnionPayConfig config = getConfig();
        Assert.isTrue(req.getPayCallbackParameter() != null
                && req.getPayCallbackParameter().get("queryId") != null
                && req.getPayCallbackParameter().get("currencyCode") != null, SystemError.PAY_22007);
        UnionPayRefundReq params = new UnionPayRefundReq(config.getMerId(), req.getRefundNumber(),
                req.getPayCallbackParameter().get("queryId").toString(), req.getRefundAmount(),
                req.getPayCallbackParameter().get("currencyCode").toString(),
                config.getRefundNoticeUrl());
        Map<String, String> paramsMap = UnionPayUtil.signData(params.toMap());
        String reqUrl = config.getRequestHostUrl() + REFUND_ADDR;
        Map<String, String> resultMap = UnionPayUtil.submitUrl(paramsMap, reqUrl);
        System.err.println("退款返回数据------------" + JSON.toJSONString(resultMap));
        UnionPayQueryTransDTO unionPayQueryTransDTO = JSON.parseObject(JSON.toJSONString(resultMap), UnionPayQueryTransDTO.class);
        return unionPayQueryTransDTO.getRespCode().equals("00");
    }


    @Override
    public RefundDetailsDTO getRefundDetail(String orderNumber, String refundNumber) {
        UnionPayQueryTransDTO unionPayQueryTransDTO = queryTrans(refundNumber);
        RefundDetailsDTO produce = RefundDetailsDTOSimpleFactory.produce(unionPayQueryTransDTO);
        return produce != null ? produce.setOrderNumber(orderNumber) : null;
    }


    @Override
    public Object refundNotify(Map<String, String> paramMap) {
        System.err.println("收到退款回调**************" + JSON.toJSONString(paramMap));
        Assert.isTrue(SDKUtil.validate(paramMap, "UTF-8"), SystemError.PAY_22005);
        UnionPayQueryTransDTO dto = JSON.parseObject(JSON.toJSONString(paramMap), UnionPayQueryTransDTO.class);
        String respCode = dto.getRespCode();
        String refundId = dto.getOrderId();
        RefundRecord record = refundRecordMapper.selectByRefundNumber(refundId);
        if (record != null) {
            BigDecimal amt = BigDecimal.valueOf(Long.valueOf(dto.getTxnAmt())).divide(BigDecimal.valueOf(100));
            Assert.isTrue(record.getRefundAmt().compareTo(amt) == 0, SystemError.PAY_22015);
            RefundRecord update = new RefundRecord();
            update.setId(record.getId());
            update.setRefundStatus("00".equals(respCode) ?
                    RefundStatus.SUCCESS.getStatus() :
                    RefundStatus.FAILURE.getStatus());
            update.setRefundTime(LocalDateTime.now());
            refundRecordMapper.updateById(update);
            //已退款的话需要同步到付款记录
            if (RefundStatus.SUCCESS.getStatus().equals(update.getRefundStatus())) {
                payRecordMapper.update(new PayRecord().setOrderStatus(PayOrderStatus.REFUNDED.getStatus()),
                        Wrappers.<PayRecord>lambdaUpdate().eq(PayRecord::getOrderNumber, record.getOrderNumber()));
            }
        }
        return "SUCCESS";
    }

    @Override
    protected boolean checkPayDetails(Map paramMap) {
        UnionPayCallback callback = JSON.parseObject(JSON.toJSONString(paramMap), UnionPayCallback.class);
        PayDetailsDTO dto = getPayDetails(callback.getOrderId());
        BigDecimal amt = BigDecimal.valueOf(Long.valueOf(callback.getTxnAmt())).divide(BigDecimal.valueOf(100));
        Assert.isTrue(amt.compareTo(dto.getOrderAmount()) == 0 &&
                dto.getPrimaryStatus().equals(callback.getRespCode()), SystemError.PAY_22017);
        return true;
    }
}
